/*
 * Copyright (C) 2009 Kristopher T Babic
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see <http://www.gnu.org/licenses>.
 */

package edu.idp.client.advrubberband;

import edu.idp.client.interfaces.Drawable;
import edu.idp.client.interfaces.Storable;
import edu.idp.shared.StoredObject;
import edu.idp.shared.utils.Convert;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Calendar;

/**
 * An abstract base class for rubberbands.<p>
 * <p/>
 * Rubberbands do their rubberbanding inside of a Component,
 * which must be specified at construction time.<p>
 *
 * @author Kristopher T Babic
 */
abstract public class Rubberband implements Drawable, Storable {
    protected Point anchorPt = new Point(0, 0);
    protected Point stretchedPt = new Point(0, 0);
    protected Point lastPt = new Point(0, 0);
    protected Point endPt = new Point(0, 0);

    private Color color = Color.black;
    private Component component;
    private boolean firstStretch = true;
    private boolean active = false;
    private Graphics buffer;
    private long timeDifference = 0;
    private long timeCreated = 0;
    private long timeDeath = StoredObject.DEFAULT_DEATH;//253401714000000l;
    private MouseAdapter mouseAdapter = null;
    private MouseMotionAdapter mouseMotionAdapter = null;

    abstract public void drawLast(Graphics g);

    abstract public void drawNext(Graphics g);

    abstract public void drawEnd(Graphics g);

    abstract public int getType();

    public Rubberband() {
    }

    public Rubberband(Component c) {
        setComponent(c);
    }

    public void setActive(boolean b) {
        active = b;
    }

    public void setComponent(Component c) {

        if (component != null && component == c)
            return;

        if (mouseAdapter == null)
            mouseAdapter = new MyMouseAdapter();
        if (mouseMotionAdapter == null)
            mouseMotionAdapter = new MyMouseMotionAdapter();

        if (component != null) {
            component.removeMouseListener(mouseAdapter);
            component.removeMouseMotionListener(mouseMotionAdapter);
        }

        component = c;
        component.addMouseListener(mouseAdapter);
        component.addMouseMotionListener(mouseMotionAdapter);

/*
		component.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent event) {
				if(isActive()) {
					anchor(event.getPoint());
				}
			}
			public void mouseClicked(MouseEvent event) {
/*				if(isActive())
					end(event.getPoint());
*
			}
			public void mouseReleased(MouseEvent event) {
				if(isActive()) {
					end(event.getPoint());
          setCreationTime(Rubberband.this.getTime());
        }
			}
		});
		component.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseDragged(MouseEvent event) {
				if(isActive())
					stretch(event.getPoint());
			}
		});
*/
    }

    public boolean isActive() {
        return active;
    }

    public Point getAnchor() {
        return anchorPt;
    }

    public Point getStretched() {
        return stretchedPt;
    }

    public Point getLast() {
        return lastPt;
    }

    public Point getEnd() {
        return endPt;
    }

    public void anchor(Point p) {
        firstStretch = true;
        anchorPt.x = p.x;
        anchorPt.y = p.y;

        stretchedPt.x = lastPt.x = anchorPt.x;
        stretchedPt.y = lastPt.y = anchorPt.y;
    }

    public void stretch(Point p) {
        lastPt.x = stretchedPt.x;
        lastPt.y = stretchedPt.y;
        stretchedPt.x = p.x;
        stretchedPt.y = p.y;

        Graphics g = component.getGraphics();
        if (g != null) {
            g.setXORMode(component.getBackground());

            if (firstStretch == true) firstStretch = false;
            else {
                drawLast(g);
            }

            drawNext(g);
            g.dispose();
        }
    }

    public void end(Point p) {
        lastPt.x = endPt.x = p.x;
        lastPt.y = endPt.y = p.y;

        Graphics g = component.getGraphics();
        if (g != null) {
            g.setXORMode(component.getBackground());
            drawLast(g);
            drawLast(buffer);
            g.setPaintMode();
            drawEnd(g);
            drawEnd(buffer);

            g.dispose();
        }
    }

    public Rectangle getBounds() {
        return new Rectangle(stretchedPt.x < anchorPt.x ? stretchedPt.x : anchorPt.x,
                             stretchedPt.y < anchorPt.y ? stretchedPt.y : anchorPt.y,
                             Math.abs(stretchedPt.x - anchorPt.x),
                             Math.abs(stretchedPt.y - anchorPt.y));
    }

    public Rectangle lastBounds() {
        return new Rectangle(
                lastPt.x < anchorPt.x ? lastPt.x : anchorPt.x,
                lastPt.y < anchorPt.y ? lastPt.y : anchorPt.y,
                Math.abs(lastPt.x - anchorPt.x),
                Math.abs(lastPt.y - anchorPt.y));
    }

    public void setColor(Color c) {
        color = c;
    }

    public Graphics getGraphic() {
        return buffer;
    }

    public void setGraphic(Graphics g) {
        buffer = g;
    }

    public Graphics getCompGraphic() {
        return component.getGraphics();
    }

    public Color getColor() {
        return color;
    }

    abstract protected String storeExtra();

    abstract protected void restoreExtra(String S);

    public void restore(StoredObject in) { //String in) {

        try {
//			Graphics g = getCompGraphic();
            Graphics g2 = getGraphic();

            //this.setColor(Convert.toColor(in.substring(2, 4)));
            this.setColor(in.getColor());   //DrawType.getColor(in));

            String points = in.getExtra();  // DrawType.getExtra(in);

            anchorPt = new Point(Convert.toInt(points.substring(0, 2)),
                                 Convert.toInt(points.substring(2, 4)));

            Point p = new Point(Convert.toInt(points.substring(4, 6)),
                                Convert.toInt(points.substring(6, 8)));
            endPt = p;
            stretchedPt = p;

            restoreExtra(points.substring(8, points.length()));

//			drawEnd(g);
            drawEnd(g2);

//			g.dispose();
            g2.dispose();
        }
        catch (Exception e) {
        }
    }

    public StoredObject store() {
/* 2-10-00
		S += Convert.fromInt(this.getType());
		S += Convert.fromColor(this.getColor());
		S += Convert.fromLong(this.getCreationTime());
		S += Convert.fromLong((long)0);
 */
        StoredObject so = new StoredObject();
        so.setType(this.getType());
        so.setColor(this.getColor());
        so.setDate(this.getCreationTime());
        so.setDeath(getDeathTime());
        so.setBounds(getBounds());

        String S = new String();
        S += Convert.fromInt(anchorPt.x);
        S += Convert.fromInt(anchorPt.y);
        S += Convert.fromInt(endPt.x);
        S += Convert.fromInt(endPt.y);
        S += storeExtra();

        so.setExtra(S);

        return so;//S;


    }

    protected long getTime() {
        return (Calendar.getInstance().getTime().getTime() + getTimeDifference());
    }

    public void setTimeDifference(long time) {
        this.timeDifference = time;
    }

    public long getTimeDifference() {
        return this.timeDifference;
    }

    public void setCreationTime(long time) {
        this.timeCreated = time;
    }

    public long getCreationTime() {
        return this.timeCreated;
    }

    public void setLifeSpan(long time) {
        this.timeDeath = time;
    }

    public long getLifeSpan() {
        return this.timeDeath;
    }

    protected long getDeathTime() {
        return ((getCreationTime() + getLifeSpan()) > 253401714000000l || (getCreationTime() + getLifeSpan()) < 0) ? 253401714000000l : getCreationTime() + getLifeSpan();
    }

    class MyMouseAdapter extends MouseAdapter {
        public void mousePressed(MouseEvent event) {
            if (isActive()) {
                anchor(event.getPoint());
            }
        }

        public void mouseClicked(MouseEvent event) {
        }

        public void mouseReleased(MouseEvent event) {
            if (isActive()) {
                end(event.getPoint());
                setCreationTime(Rubberband.this.getTime());
            }
        }
    }

    class MyMouseMotionAdapter extends MouseMotionAdapter {
        public void mouseDragged(MouseEvent event) {
            if (isActive())
                stretch(event.getPoint());
        }
    }

}
